package com.sinozo.advice;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.sinozo.domain.ResponseDTO;
import com.sinozo.errcode.CommonCode;
import com.sinozo.exception.AbstractException;
import com.sinozo.utils.StackUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.ObjectError;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;
import org.springframework.web.multipart.support.MissingServletRequestPartException;

import javax.servlet.http.HttpServletRequest;
import java.sql.BatchUpdateException;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 全局异常
 *
 * @author zhehen.lu
 * @date 2025/11/6 01:43
 */
@Slf4j
public class ControllerErrorAdvice {

    /**
     * 处理AbstractException异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(AbstractException.class)
    public ResponseDTO<?> handleAbstractException(AbstractException e, HttpServletRequest request) {
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(e.getCode(), e.getMsg());

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * 方法不被允许
     * @param e
     * @param request
     * @return {@link ResponseDTO<?>}
     * @date 2023/1/9 14:33
     * @author zhehen.lu
     */
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseDTO<?> handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e, HttpServletRequest request) {
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.METHOD_NOT_ALLOW);

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * 处理sql批量更新错误
     * @param e
     * @param request
     * @return {@link ResponseDTO<?>}
     * @date 2023/1/9 14:36
     * @author zhehen.lu
     */
    @ExceptionHandler(BatchUpdateException.class)
    public ResponseDTO<?> handleBatchUpdateException(BatchUpdateException e, HttpServletRequest request) {
        printStackTrace(e);
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.MYSQL_UPDATE_ERROR.getCode(), CommonCode.MYSQL_UPDATE_ERROR.getMsg());

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * 处理统一的异常
     * @param e
     * @param request
     * @return
     */
    @ExceptionHandler(Exception.class)
    public ResponseDTO<?> handleException(Exception e, HttpServletRequest request) {
        printStackTrace(e);
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.SERVER_ERROR.getCode(), CommonCode.SERVER_ERROR.getMsg());

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * 处理上传空文件异常
     * @param e 上传空文件异常
     * @param request 请求
     * @return {@link ResponseDTO<?>}
     * @date  2022/9/27 15:00
     * @author zsx
     */
    @ExceptionHandler(MissingServletRequestPartException.class)
    public ResponseDTO<?> handleUploadEmptyFileException(MissingServletRequestPartException e, HttpServletRequest request){
        printStackTrace(e);
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.UPLOAD_FILE_IS_EMPTY);
        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * @Description 请求参数不合法
     * @Created by zhehen.lu
     * @date 2022/7/25 19:56
     * @param e
     * @param request
     * @Return
     */
    @ExceptionHandler(MissingServletRequestParameterException.class)
    public ResponseDTO<?> handleParamException(MissingServletRequestParameterException e, HttpServletRequest request){
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR);

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * @Description http请求， 转换参数错误
     * @Created by zhehen.lu
     * @date 2022/7/25 19:57
     * @param e
     * @param request
     * @Return
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseDTO<?> handleHttpMessageNotReadableException(HttpMessageNotReadableException e, HttpServletRequest request){
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR);
        if (e.getCause() instanceof InvalidFormatException) {
            // 参数转换异常 比如 int 转为String
            InvalidFormatException invalidFormatException = (InvalidFormatException) e.getCause();
            try {
                List<JsonMappingException.Reference> fieldRefList = invalidFormatException.getPath();
                if (CollectionUtil.isNotEmpty(fieldRefList)) {
                    String errorField = fieldRefList.stream().map(JsonMappingException.Reference::getFieldName)
                            .collect(Collectors.joining(","));
                    responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR.getCode(),
                            errorField + " 属性值类型或格式错误");
                } else {
                    String s = invalidFormatException.getValue().toString();
                    responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR.getCode(), s + " 值格式错误");
                }

            } catch (Exception ex) {
            }
        } else if (e.getCause() instanceof JsonMappingException) {
            JsonMappingException cause = (JsonMappingException) e.getCause();
            JsonMappingException.Reference reference = cause.getPath().stream().findFirst().orElse(null);
            if (reference != null) {
                responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR.getCode(), reference.getFieldName() + "格式错误");
            }
        } else if (e.getCause() instanceof JsonParseException) {
            responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR.getCode(), "参数json格式错误");
        }

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * @Description 请求参数不匹配错误
     * @Created by zhehen.lu
     * @date 2022/7/26 9:07
     * @param e
     * @param request
     * @Return
     */
    @ExceptionHandler({MethodArgumentTypeMismatchException.class})
    public ResponseDTO<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException e, HttpServletRequest request){
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR.getCode(), "参数不合法");

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * @Description 上传文件， 没传参数名
     * @Created by zhehen.lu
     * @date 2022/7/26 14:33
     * @param e
     * @Return
     */
    @ExceptionHandler(HttpMediaTypeNotSupportedException.class)
    public ResponseDTO<?> handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e){
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.PARAM_ERROR.getCode(), "参数不合法");

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /***
     * @Description 参数校验错误
     * @Created by zsx
     * @date 2022/8/10 16:50
     * @param ex
     * @Return
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseDTO<String> methodArgumentNotValidException(MethodArgumentNotValidException ex) {
        // TODO这里需要打印入参的信息
        List<String> errorList = new ArrayList<>();

        if (Objects.nonNull(ex.getBindingResult())
                && CollectionUtil.isNotEmpty(ex.getBindingResult().getAllErrors())) {
            for (ObjectError error : ex.getBindingResult().getAllErrors()) {
                if (StrUtil.isNotBlank(error.getDefaultMessage())) {
                    errorList.add(error.getDefaultMessage());
                }

            }
        }
        String errorMsg = errorList.stream().collect(Collectors.joining(";"));
        ResponseDTO<String> response = ResponseDTO.ofError(CommonCode.PARAM_ERROR.getCode(),errorMsg);
        saveResponseLog(response);
        return response;
    }

    /**
     * IO报错
     * @param e
     * @param request
     * @return {@link ResponseDTO<?>}
     * @date 2023/1/9 14:43
     * @author zhehen.lu
     */
    @ExceptionHandler(IORuntimeException.class)
    public ResponseDTO<?> handleIORuntimeException(IORuntimeException e, HttpServletRequest request){
        printStackTrace(e);
        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.IO_ERROR);

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * 文件上传太大
     * @param e
     * @param request
     * @return {@link ResponseDTO<?>}
     * @date 2023/3/1 15:35
     * @author zhehen.lu
     */
    @ExceptionHandler(MultipartException.class)
    public ResponseDTO<?> handleMultipartException(MultipartException e, HttpServletRequest request){
        printStackTrace(e);

        ResponseDTO<Object> responseDTO = ResponseDTO.ofError(CommonCode.UPLOAD_FILE_TOO_LARGE);

        saveResponseLog(responseDTO);
        return responseDTO;
    }

    /**
     * @Description 打印堆栈信息
     * @Created by zhehen.lu
     * @date 2022/8/2 16:41
     * @param e
     * @Return
     */
    protected void printStackTrace(Exception e) {
        String s = StackUtil.stackTraceToString(e);
        log.error("errMsg:{}", s);
    }

    /**
     * 保存返回日志
     * @param responseDTO
     */
    protected void saveResponseLog(ResponseDTO responseDTO) {
        log.info("处理有异常，返回数据：{}", JSON.toJSONString(responseDTO));
    }

}
